; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --check-attributes --check-globals
; RUN: opt -aa-pipeline=basic-aa -passes=attributor -attributor-manifest-internal  -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=CHECK,TUNIT
; RUN: opt -aa-pipeline=basic-aa -passes=attributor-cgscc -attributor-manifest-internal  -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC

; Determine dereference-ability before unused loads get deleted:
; https://bugs.llvm.org/show_bug.cgi?id=21780

define <4 x double> @PR21780(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: read)
; CHECK-LABEL: define {{[^@]+}}@PR21780
; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 8 dereferenceable(32) [[PTR:%.*]]) #[[ATTR0:[0-9]+]] {
; CHECK-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr inbounds double, ptr [[PTR]], i64 1
; CHECK-NEXT:    [[ARRAYIDX2:%.*]] = getelementptr inbounds double, ptr [[PTR]], i64 2
; CHECK-NEXT:    [[ARRAYIDX3:%.*]] = getelementptr inbounds double, ptr [[PTR]], i64 3
; CHECK-NEXT:    [[T0:%.*]] = load double, ptr [[PTR]], align 8
; CHECK-NEXT:    [[T1:%.*]] = load double, ptr [[ARRAYIDX1]], align 8
; CHECK-NEXT:    [[T2:%.*]] = load double, ptr [[ARRAYIDX2]], align 8
; CHECK-NEXT:    [[T3:%.*]] = load double, ptr [[ARRAYIDX3]], align 8
; CHECK-NEXT:    [[VECINIT0:%.*]] = insertelement <4 x double> poison, double [[T0]], i32 0
; CHECK-NEXT:    [[VECINIT1:%.*]] = insertelement <4 x double> [[VECINIT0]], double [[T1]], i32 1
; CHECK-NEXT:    [[VECINIT2:%.*]] = insertelement <4 x double> [[VECINIT1]], double [[T2]], i32 2
; CHECK-NEXT:    [[VECINIT3:%.*]] = insertelement <4 x double> [[VECINIT2]], double [[T3]], i32 3
; CHECK-NEXT:    [[SHUFFLE:%.*]] = shufflevector <4 x double> [[VECINIT3]], <4 x double> [[VECINIT3]], <4 x i32> <i32 0, i32 0, i32 2, i32 2>
; CHECK-NEXT:    ret <4 x double> [[SHUFFLE]]
;

  ; GEP of index 0 is simplified away.
  %arrayidx1 = getelementptr inbounds double, ptr %ptr, i64 1
  %arrayidx2 = getelementptr inbounds double, ptr %ptr, i64 2
  %arrayidx3 = getelementptr inbounds double, ptr %ptr, i64 3

  %t0 = load double, ptr %ptr, align 8
  %t1 = load double, ptr %arrayidx1, align 8
  %t2 = load double, ptr %arrayidx2, align 8
  %t3 = load double, ptr %arrayidx3, align 8

  %vecinit0 = insertelement <4 x double> poison, double %t0, i32 0
  %vecinit1 = insertelement <4 x double> %vecinit0, double %t1, i32 1
  %vecinit2 = insertelement <4 x double> %vecinit1, double %t2, i32 2
  %vecinit3 = insertelement <4 x double> %vecinit2, double %t3, i32 3
  %shuffle = shufflevector <4 x double> %vecinit3, <4 x double> %vecinit3, <4 x i32> <i32 0, i32 0, i32 2, i32 2>
  ret <4 x double> %shuffle
}


define double @PR21780_only_access3_with_inbounds(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: read)
; CHECK-LABEL: define {{[^@]+}}@PR21780_only_access3_with_inbounds
; CHECK-SAME: (ptr nocapture nofree nonnull readonly align 8 dereferenceable(32) [[PTR:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:    [[ARRAYIDX3:%.*]] = getelementptr inbounds double, ptr [[PTR]], i64 3
; CHECK-NEXT:    [[T3:%.*]] = load double, ptr [[ARRAYIDX3]], align 8
; CHECK-NEXT:    ret double [[T3]]
;

  %arrayidx3 = getelementptr inbounds double, double* %ptr, i64 3
  %t3 = load double, ptr %arrayidx3, align 8
  ret double %t3
}

define double @PR21780_only_access3_without_inbounds(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: read)
; CHECK-LABEL: define {{[^@]+}}@PR21780_only_access3_without_inbounds
; CHECK-SAME: (ptr nocapture nofree readonly align 8 [[PTR:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:    [[ARRAYIDX3:%.*]] = getelementptr double, ptr [[PTR]], i64 3
; CHECK-NEXT:    [[T3:%.*]] = load double, ptr [[ARRAYIDX3]], align 8
; CHECK-NEXT:    ret double [[T3]]
;
  %arrayidx3 = getelementptr double, double* %ptr, i64 3
  %t3 = load double, ptr %arrayidx3, align 8
  ret double %t3
}

define double @PR21780_without_inbounds(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: read)
; CHECK-LABEL: define {{[^@]+}}@PR21780_without_inbounds
; CHECK-SAME: (ptr nocapture nofree noundef nonnull readonly align 8 dereferenceable(32) [[PTR:%.*]]) #[[ATTR0]] {
; CHECK-NEXT:    [[ARRAYIDX3:%.*]] = getelementptr double, ptr [[PTR]], i64 3
; CHECK-NEXT:    [[T3:%.*]] = load double, ptr [[ARRAYIDX3]], align 8
; CHECK-NEXT:    ret double [[T3]]
;

  %arrayidx1 = getelementptr double, double* %ptr, i64 1
  %arrayidx2 = getelementptr double, ptr %ptr, i64 2
  %arrayidx3 = getelementptr double, ptr %ptr, i64 3

  %t0 = load double, ptr %ptr, align 8
  %t1 = load double, ptr %arrayidx1, align 8
  %t2 = load double, ptr %arrayidx2, align 8
  %t3 = load double, ptr %arrayidx3, align 8

  ret double %t3
}

; Unsimplified, but still valid. Also, throw in some bogus arguments.

define void @gep0(ptr %unused, ptr %other, ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: readwrite)
; CHECK-LABEL: define {{[^@]+}}@gep0
; CHECK-SAME: (ptr nocapture nofree readnone [[UNUSED:%.*]], ptr nocapture nofree noundef nonnull writeonly dereferenceable(1) [[OTHER:%.*]], ptr nocapture nofree nonnull readonly dereferenceable(3) [[PTR:%.*]]) #[[ATTR1:[0-9]+]] {
; CHECK-NEXT:    [[ARRAYIDX2:%.*]] = getelementptr i8, ptr [[PTR]], i64 2
; CHECK-NEXT:    [[T2:%.*]] = load i8, ptr [[ARRAYIDX2]], align 1
; CHECK-NEXT:    store i8 [[T2]], ptr [[OTHER]], align 1
; CHECK-NEXT:    ret void
;
  %arrayidx0 = getelementptr i8, i8* %ptr, i64 0
  %arrayidx1 = getelementptr i8, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i8, ptr %ptr, i64 2
  %t0 = load i8, ptr %arrayidx0
  %t1 = load i8, ptr %arrayidx1
  %t2 = load i8, ptr %arrayidx2
  store i8 %t2, ptr %other
  ret void
}

; Order of accesses does not change computation.
; Multiple arguments may be dereferenceable.

define void @ordering(ptr %ptr1, ptr %ptr2) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@ordering
; CHECK-SAME: (ptr nocapture nofree nonnull readnone dereferenceable(3) [[PTR1:%.*]], ptr nocapture nofree nonnull readnone align 4 dereferenceable(8) [[PTR2:%.*]]) #[[ATTR2:[0-9]+]] {
; CHECK-NEXT:    ret void
;
  %a20 = getelementptr i32, i32* %ptr2, i64 0
  %a12 = getelementptr i8, ptr %ptr1, i64 2
  %t12 = load i8, ptr %a12
  %a11 = getelementptr i8, ptr %ptr1, i64 1
  %t20 = load i32, ptr %a20
  %t10 = load i8, ptr %ptr1
  %t11 = load i8, ptr %a11
  %a21 = getelementptr i32, ptr %ptr2, i64 1
  %t21 = load i32, ptr %a21
  ret void
}

; Not in entry block.

define void @not_entry_but_guaranteed_to_execute(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@not_entry_but_guaranteed_to_execute
; CHECK-SAME: (ptr nocapture nofree nonnull readnone dereferenceable(3) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[EXIT:%.*]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %exit
exit:
  %arrayidx1 = getelementptr i8, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i8, ptr %ptr, i64 2
  %t0 = load i8, ptr %ptr
  %t1 = load i8, ptr %arrayidx1
  %t2 = load i8, ptr %arrayidx2
  ret void
}

; Not in entry block and not guaranteed to execute.

define void @not_entry_not_guaranteed_to_execute(ptr %ptr, i1 %cond) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@not_entry_not_guaranteed_to_execute
; CHECK-SAME: (ptr nocapture nofree readnone [[PTR:%.*]], i1 noundef [[COND:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br i1 [[COND]], label [[LOADS:%.*]], label [[EXIT:%.*]]
; CHECK:       loads:
; CHECK-NEXT:    ret void
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br i1 %cond, label %loads, label %exit
loads:
  %arrayidx1 = getelementptr i8, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i8, ptr %ptr, i64 2
  %t0 = load i8, ptr %ptr
  %t1 = load i8, ptr %arrayidx1
  %t2 = load i8, ptr %arrayidx2
  ret void
exit:
  ret void
}

; The last load may not execute, so derefenceable bytes only covers the 1st two loads.

define void @partial_in_entry(ptr %ptr, i1 %cond) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@partial_in_entry
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 2 dereferenceable(4) [[PTR:%.*]], i1 noundef [[COND:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br i1 [[COND]], label [[LOADS:%.*]], label [[EXIT:%.*]]
; CHECK:       loads:
; CHECK-NEXT:    ret void
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  %arrayidx1 = getelementptr i16, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i16, ptr %ptr, i64 2
  %t0 = load i16, ptr %ptr
  %t1 = load i16, ptr %arrayidx1
  br i1 %cond, label %loads, label %exit
loads:
  %t2 = load i16, ptr %arrayidx2
  ret void
exit:
  ret void
}

; The volatile load can't be used to prove a non-volatile access is allowed.
; The 2nd and 3rd loads may never execute.

define void @volatile_is_not_dereferenceable(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nounwind willreturn memory(argmem: readwrite)
; CHECK-LABEL: define {{[^@]+}}@volatile_is_not_dereferenceable
; CHECK-SAME: (ptr nofree align 2 [[PTR:%.*]]) #[[ATTR3:[0-9]+]] {
; CHECK-NEXT:    [[T0:%.*]] = load volatile i16, ptr [[PTR]], align 2
; CHECK-NEXT:    ret void
;
  %arrayidx0 = getelementptr i16, i16* %ptr, i64 0
  %arrayidx1 = getelementptr i16, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i16, ptr %ptr, i64 2
  %t0 = load volatile i16, ptr %arrayidx0
  %t1 = load i16, ptr %arrayidx1
  %t2 = load i16, ptr %arrayidx2
  ret void
}

; TODO: We should allow inference for atomic (but not volatile) ops.

define void @atomic_is_alright(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@atomic_is_alright
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 2 dereferenceable(6) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx0 = getelementptr i16, i16* %ptr, i64 0
  %arrayidx1 = getelementptr i16, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i16, ptr %ptr, i64 2
  %t0 = load atomic i16, ptr %arrayidx0 unordered, align 2
  %t1 = load i16, ptr %arrayidx1
  %t2 = load i16, ptr %arrayidx2
  ret void
}

declare void @may_not_return()

define void @not_guaranteed_to_transfer_execution(ptr %ptr) {
; CHECK-LABEL: define {{[^@]+}}@not_guaranteed_to_transfer_execution
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 2 dereferenceable(2) [[PTR:%.*]]) {
; CHECK-NEXT:    call void @may_not_return()
; CHECK-NEXT:    ret void
;
  %arrayidx0 = getelementptr i16, i16* %ptr, i64 0
  %arrayidx1 = getelementptr i16, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i16, ptr %ptr, i64 2
  %t0 = load i16, ptr %arrayidx0
  call void @may_not_return()
  %t1 = load i16, ptr %arrayidx1
  %t2 = load i16, ptr %arrayidx2
  ret void
}

; We must have consecutive accesses.

define void @variable_gep_index(ptr %unused, ptr %ptr, i64 %variable_index) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@variable_gep_index
; CHECK-SAME: (ptr nocapture nofree readnone [[UNUSED:%.*]], ptr nocapture nofree nonnull readnone dereferenceable(1) [[PTR:%.*]], i64 [[VARIABLE_INDEX:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx1 = getelementptr i8, i8* %ptr, i64 %variable_index
  %arrayidx2 = getelementptr i8, ptr %ptr, i64 2
  %t0 = load i8, ptr %ptr
  %t1 = load i8, ptr %arrayidx1
  %t2 = load i8, ptr %arrayidx2
  ret void
}

; Deal with >1 GEP index.

define void @multi_index_gep(ptr %ptr) {
; FIXME: %ptr should be dereferenceable(4)
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@multi_index_gep
; CHECK-SAME: (ptr nocapture nofree nonnull readnone dereferenceable(1) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx00 = getelementptr <4 x i8>, <4 x i8>* %ptr, i64 0, i64 0
  %t0 = load i8, ptr %arrayidx00
  ret void
}

; Could round weird bitwidths down?

define void @not_byte_multiple(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@not_byte_multiple
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 2 dereferenceable(2) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx0 = getelementptr i9, i9* %ptr, i64 0
  %t0 = load i9, ptr %arrayidx0
  ret void
}

; Missing direct access from the pointer.

define void @no_pointer_deref(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@no_pointer_deref
; CHECK-SAME: (ptr nocapture nofree readnone align 2 [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx1 = getelementptr i16, i16* %ptr, i64 1
  %arrayidx2 = getelementptr i16, ptr %ptr, i64 2
  %t1 = load i16, ptr %arrayidx1
  %t2 = load i16, ptr %arrayidx2
  ret void
}

; Out-of-order is ok, but missing access concludes dereferenceable range.

define void @non_consecutive(ptr %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@non_consecutive
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(8) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx1 = getelementptr i32, i32* %ptr, i64 1
  %arrayidx3 = getelementptr i32, ptr %ptr, i64 3
  %t1 = load i32, ptr %arrayidx1
  %t0 = load i32, ptr %ptr
  %t3 = load i32, ptr %arrayidx3
  ret void
}

; Improve on existing dereferenceable attribute.

define void @more_bytes(ptr dereferenceable(8) %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@more_bytes
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(16) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx3 = getelementptr i32, i32* %ptr, i64 3
  %arrayidx1 = getelementptr i32, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i32, ptr %ptr, i64 2
  %t3 = load i32, ptr %arrayidx3
  %t1 = load i32, ptr %arrayidx1
  %t2 = load i32, ptr %arrayidx2
  %t0 = load i32, ptr %ptr
  ret void
}

; Improve on existing dereferenceable_or_null attribute.

define void @more_bytes_and_not_null(ptr dereferenceable_or_null(8) %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@more_bytes_and_not_null
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(16) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx3 = getelementptr i32, i32* %ptr, i64 3
  %arrayidx1 = getelementptr i32, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i32, ptr %ptr, i64 2
  %t3 = load i32, ptr %arrayidx3
  %t1 = load i32, ptr %arrayidx1
  %t2 = load i32, ptr %arrayidx2
  %t0 = load i32, ptr %ptr
  ret void
}

; But don't pessimize existing dereferenceable attribute.

define void @better_bytes(ptr dereferenceable(100) %ptr) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@better_bytes
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(100) [[PTR:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %arrayidx3 = getelementptr i32, i32* %ptr, i64 3
  %arrayidx1 = getelementptr i32, ptr %ptr, i64 1
  %arrayidx2 = getelementptr i32, ptr %ptr, i64 2
  %t3 = load i32, ptr %arrayidx3
  %t1 = load i32, ptr %arrayidx1
  %t2 = load i32, ptr %arrayidx2
  %t0 = load i32, ptr %ptr
  ret void
}

define void @bitcast(ptr %arg) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@bitcast
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(8) [[ARG:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %ptr = bitcast i32* %arg to float*
  %arrayidx1 = getelementptr float, ptr %ptr, i64 1
  %t0 = load float, ptr %ptr
  %t1 = load float, ptr %arrayidx1
  ret void
}

define void @bitcast_different_sizes(ptr %arg1, ptr %arg2) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@bitcast_different_sizes
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(12) [[ARG1:%.*]], ptr nocapture nofree nonnull readnone align 4 dereferenceable(16) [[ARG2:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %ptr1 = bitcast double* %arg1 to float*
  %a11 = getelementptr float, ptr %ptr1, i64 1
  %a12 = getelementptr float, ptr %ptr1, i64 2
  %ld10 = load float, ptr %ptr1
  %ld11 = load float, ptr %a11
  %ld12 = load float, ptr %a12

  %a21 = getelementptr i64, ptr %arg2, i64 1
  %ld20 = load i64, ptr %arg2
  %ld21 = load i64, ptr %a21
  ret void
}

define void @negative_offset(ptr %arg) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(none)
; CHECK-LABEL: define {{[^@]+}}@negative_offset
; CHECK-SAME: (ptr nocapture nofree nonnull readnone align 4 dereferenceable(4) [[ARG:%.*]]) #[[ATTR2]] {
; CHECK-NEXT:    ret void
;
  %ptr = bitcast i32* %arg to float*
  %arrayidx1 = getelementptr float, ptr %ptr, i64 -1
  %t0 = load float, ptr %ptr
  %t1 = load float, ptr %arrayidx1
  ret void
}

define void @stores(ptr %arg) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@stores
; CHECK-SAME: (ptr nocapture nofree nonnull writeonly align 4 dereferenceable(8) [[ARG:%.*]]) #[[ATTR4:[0-9]+]] {
; CHECK-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr float, ptr [[ARG]], i64 1
; CHECK-NEXT:    store float 1.000000e+00, ptr [[ARG]], align 4
; CHECK-NEXT:    store float 2.000000e+00, ptr [[ARRAYIDX1]], align 4
; CHECK-NEXT:    ret void
;
  %ptr = bitcast i32* %arg to float*
  %arrayidx1 = getelementptr float, ptr %ptr, i64 1
  store float 1.0, ptr %ptr
  store float 2.0, ptr %arrayidx1
  ret void
}

define void @load_store(ptr %arg) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@load_store
; CHECK-SAME: (ptr nocapture nofree nonnull writeonly align 4 dereferenceable(8) [[ARG:%.*]]) #[[ATTR4]] {
; CHECK-NEXT:    [[ARRAYIDX1:%.*]] = getelementptr float, ptr [[ARG]], i64 1
; CHECK-NEXT:    store float 2.000000e+00, ptr [[ARRAYIDX1]], align 4
; CHECK-NEXT:    ret void
;
  %ptr = bitcast i32* %arg to float*
  %arrayidx1 = getelementptr float, ptr %ptr, i64 1
  %t1 = load float, ptr %ptr
  store float 2.0, ptr %arrayidx1
  ret void
}

define void @different_size1(ptr %arg) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@different_size1
; CHECK-SAME: (ptr nocapture nofree noundef nonnull writeonly align 8 dereferenceable(8) [[ARG:%.*]]) #[[ATTR4]] {
; CHECK-NEXT:    store double 0.000000e+00, ptr [[ARG]], align 8
; CHECK-NEXT:    store i32 0, ptr [[ARG]], align 8
; CHECK-NEXT:    ret void
;
  %arg-cast = bitcast i32* %arg to double*
  store double 0.000000e+00, ptr %arg-cast
  store i32 0, ptr %arg
  ret void
}

define void @different_size2(ptr %arg) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@different_size2
; CHECK-SAME: (ptr nocapture nofree noundef nonnull writeonly align 8 dereferenceable(8) [[ARG:%.*]]) #[[ATTR4]] {
; CHECK-NEXT:    store i32 0, ptr [[ARG]], align 8
; CHECK-NEXT:    store double 0.000000e+00, ptr [[ARG]], align 8
; CHECK-NEXT:    ret void
;
  store i32 0, i32* %arg
  store double 0.000000e+00, ptr %arg
  ret void
}

; Make use of MustBeExecuted Explorer
;
; [CFG]
; entry
;  / \
; l1 l2
; | X |
; l3 l4
;  \ /
;  l5
;  / \
; l6 l7
;  \ /
;  end
; According to the above CFG, we can see that instructions in l5 Block must be executed.
; Therefore, %p must be dereferenced.
;
; ATTRIBUTOR_CGSCC_NPM-LABEL: define i32 @require_cfg_analysis(i32 %c, i32* {{.*}} dereferenceable(4) %p)
define i32 @require_cfg_analysis(i32 %c, ptr %p) {
; CHECK: Function Attrs: nofree norecurse nosync nounwind willreturn memory(argmem: write)
; CHECK-LABEL: define {{[^@]+}}@require_cfg_analysis
; CHECK-SAME: (i32 [[C:%.*]], ptr nocapture nofree nonnull writeonly align 4 dereferenceable(4) [[P:%.*]]) #[[ATTR4]] {
; CHECK-NEXT:    [[TOBOOL1:%.*]] = icmp eq i32 [[C]], 0
; CHECK-NEXT:    br i1 [[TOBOOL1]], label [[L1:%.*]], label [[L2:%.*]]
; CHECK:       l1:
; CHECK-NEXT:    [[TOBOOL2:%.*]] = icmp eq i32 [[C]], 1
; CHECK-NEXT:    br i1 [[TOBOOL2]], label [[L3:%.*]], label [[L4:%.*]]
; CHECK:       l2:
; CHECK-NEXT:    [[TOBOOL3:%.*]] = icmp eq i32 [[C]], 2
; CHECK-NEXT:    br i1 [[TOBOOL3]], label [[L3]], label [[L4]]
; CHECK:       l3:
; CHECK-NEXT:    br label [[L5:%.*]]
; CHECK:       l4:
; CHECK-NEXT:    br label [[L5]]
; CHECK:       l5:
; CHECK-NEXT:    [[TOBOOL4:%.*]] = icmp eq i32 [[C]], 4
; CHECK-NEXT:    br i1 [[TOBOOL4]], label [[L6:%.*]], label [[L7:%.*]]
; CHECK:       l6:
; CHECK-NEXT:    store i32 0, ptr [[P]], align 4
; CHECK-NEXT:    br label [[END:%.*]]
; CHECK:       l7:
; CHECK-NEXT:    store i32 1, ptr [[P]], align 4
; CHECK-NEXT:    br label [[END]]
; CHECK:       end:
; CHECK-NEXT:    ret i32 1
;
  %tobool1 = icmp eq i32 %c, 0
  br i1 %tobool1, label %l1, label %l2
l1:
  %tobool2 = icmp eq i32 %c, 1
  br i1 %tobool2, label %l3, label %l4
l2:
  %tobool3 = icmp eq i32 %c, 2
  br i1 %tobool3, label %l3, label %l4
l3:
  br label %l5
l4:
  br label %l5
l5:
  %tobool4 = icmp eq i32 %c, 4
  br i1 %tobool4, label %l6, label %l7
l6:
  store i32 0, ptr %p
  br label %end
l7:
  store i32 1, ptr %p
  br label %end
end:
  ret i32 1
}
;.
; CHECK: attributes #[[ATTR0]] = { nofree norecurse nosync nounwind willreturn memory(argmem: read) }
; CHECK: attributes #[[ATTR1]] = { nofree norecurse nosync nounwind willreturn memory(argmem: readwrite) }
; CHECK: attributes #[[ATTR2]] = { nofree norecurse nosync nounwind willreturn memory(none) }
; CHECK: attributes #[[ATTR3]] = { nofree norecurse nounwind willreturn memory(argmem: readwrite) }
; CHECK: attributes #[[ATTR4]] = { nofree norecurse nosync nounwind willreturn memory(argmem: write) }
;.
;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
; CGSCC: {{.*}}
; TUNIT: {{.*}}
